#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
配置管理器 - 完全基于配置文件的参数管理

这个模块解决了当前代码中硬编码配置的问题，提供：
1. 完全的配置文件驱动
2. 配置验证和默认值处理
3. 可扩展的通道配置
4. 错误处理和配置修复建议


核心功能：
    - 配置文件驱动：通过JSON配置文件管理所有参数，避免代码中硬编码
    - 配置验证：自动验证配置的完整性和正确性
    - 默认配置：提供完整的默认配置模板

关键数据结构：
    - ChannelConfig：通道配置（名称、位值、持续时间等）
    - ProcessingConfig：处理配置（时间对齐、窗口设置等）
    - FileConfig：文件路径配置

工作流程：
    1. 加载JSON配置文件，如不存在则使用默认配置
    2. 合并用户配置与默认配置
    3. 验证配置的有效性（文件存在性、参数合理性等）
    4. 提供结构化的配置对象给其他模块使用
"""

import json
import os
from typing import Dict, Any, Optional, List, Tuple
from dataclasses import dataclass
from pathlib import Path

@dataclass
class ChannelConfig:
    """通道配置类"""
    name: str
    bit_position: int
    duration: float
    description: Optional[str] = None
    enabled: bool = True

@dataclass
class ProcessingConfig:
    """处理配置类"""
    alignment_time: Optional[float] = None
    alignment_channel: Optional[int] = None
    time_window_before: float = 0.0
    time_window_after: float = 0.0
    sampling_rate_check: bool = True

@dataclass  
class FileConfig:
    """文件配置类"""
    data_path: str
    csv_path: Optional[str] = None
    audio_path: Optional[str] = None
    video_path: Optional[str] = None
    output_dir: Optional[str] = None

class ConfigManager:
    """
    配置管理器 - 处理所有配置相关的操作
    
    主要功能:
    1. 从JSON文件加载配置
    2. 验证配置的完整性和正确性
    3. 提供默认配置
    4. 支持配置的动态修改和保存
    """
    
    # 默认配置模板
    DEFAULT_CONFIG = {
        "paradigm_type": "auto",  # "auto"(有CSV=movement,无CSV=port), "movement", "corpus", "port"
        "file_config": {
            "data_path": "",
            "csv_path": None,
            "audio_path": None,
            "video_path": None,
            "output_dir": None
        },
        "channel_mapping": {
            "1": {"name": "通道1", "duration": 1.0, "description": "准备阶段", "enabled": True},
            "2": {"name": "通道2", "duration": 1.0, "description": "执行阶段", "enabled": True}, 
            "4": {"name": "通道3", "duration": 1.0, "description": "复位阶段", "enabled": True},
            "8": {"name": "通道4", "duration": 1.0, "description": "基准信号", "enabled": True}
        },
        "processing_config": {
            "alignment_time": None,
            "alignment_channel": None,
            "time_window_before": 0.0,
            "time_window_after": 0.0,
            "sampling_rate_check": True
        }
    }
    
    def __init__(self, config_path: str = "config.json"):
        """
        初始化配置管理器
        
        Args:
            config_path: 配置文件路径
        """
        self.config_path = Path(config_path)
        self.config_data = {}
        self.validation_errors = []
        self.validation_warnings = []
        
        # 尝试加载配置
        self.load_config()
    
    def load_config(self) -> bool:
        """
        加载配置文件
        
        Returns:
            bool: 是否成功加载
        """
        try:
            if self.config_path.exists():
                with open(self.config_path, 'r', encoding='utf-8') as f:
                    user_config = json.load(f)
                
                # 合并用户配置和默认配置
                self.config_data = self._merge_config(self.DEFAULT_CONFIG.copy(), user_config)
                print(f"成功加载配置文件: {self.config_path}")
            else:
                print(f"配置文件不存在，使用默认配置: {self.config_path}")
                self.config_data = self.DEFAULT_CONFIG.copy()
                self.save_config()  # 保存默认配置
            
            # 验证配置
            return self.validate_config()
            
        except json.JSONDecodeError as e:
            print(f"配置文件JSON格式错误: {e}")
            self.validation_errors.append(f"JSON格式错误: {e}")
            return False
        except Exception as e:
            print(f"加载配置文件失败: {e}")
            self.validation_errors.append(f"加载失败: {e}")
            return False
    
    def save_config(self) -> bool:
        """
        保存配置到文件
        
        Returns:
            bool: 是否成功保存
        """
        try:
            with open(self.config_path, 'w', encoding='utf-8') as f:
                json.dump(self.config_data, f, indent=4, ensure_ascii=False)
            print(f"配置已保存到: {self.config_path}")
            return True
        except Exception as e:
            print(f"保存配置失败: {e}")
            return False
    
    def _merge_config(self, default_config: Dict, user_config: Dict) -> Dict:
        """
        递归合并默认配置和用户配置
        
        Args:
            default_config: 默认配置
            user_config: 用户配置
            
        Returns:
            Dict: 合并后的配置
        """
        for key, value in user_config.items():
            if isinstance(value, dict) and key in default_config:
                default_config[key] = self._merge_config(default_config[key], value)
            else:
                default_config[key] = value
        return default_config
    
    def validate_config(self) -> bool:
        """
        验证配置的完整性和正确性
        
        Returns:
            bool: 配置是否有效
        """
        self.validation_errors = []
        self.validation_warnings = []
        
        # 验证文件配置
        self._validate_file_config()
        
        # 验证通道配置
        self._validate_channel_config()
        
        # 验证处理配置
        self._validate_processing_config()
        
        # 输出验证结果
        if self.validation_errors:
            print("[X] 配置验证失败:")
            for error in self.validation_errors:
                print(f"   - {error}")
            return False
        
        if self.validation_warnings:
            print("[!] 配置警告:")
            for warning in self.validation_warnings:
                print(f"   - {warning}")
        
        print("[√] 配置验证通过")
        return True
    
    def _validate_file_config(self):
        """验证文件配置"""
        file_config = self.config_data.get("file_config", {})
        
        # 检查必需的数据文件
        data_path = file_config.get("data_path", "")
        if not data_path:
            self.validation_errors.append("缺少必需的data_path配置")
        elif not Path(data_path).exists():
            self.validation_errors.append(f"数据文件不存在: {data_path}")
        
        # 检查可选文件
        optional_files = ["csv_path", "audio_path", "video_path"]
        for file_key in optional_files:
            file_path = file_config.get(file_key)
            if file_path and not Path(file_path).exists():
                self.validation_warnings.append(f"{file_key}文件不存在: {file_path}")
    
    def _validate_channel_config(self):
        """验证通道配置"""
        channel_mapping = self.config_data.get("channel_mapping", {})
        
        if not channel_mapping:
            self.validation_errors.append("缺少channel_mapping配置")
            return
        
        # 验证通道位值
        valid_bits = [1, 2, 4, 8, 16, 32, 64, 128]  # 支持8位
        used_names = set()
        
        for bit_str, channel_config in channel_mapping.items():
            try:
                bit_value = int(bit_str)
                if bit_value not in valid_bits:
                    self.validation_errors.append(f"无效的通道位值: {bit_value}")
                
                # 验证通道配置结构
                if not isinstance(channel_config, dict):
                    self.validation_errors.append(f"通道{bit_str}配置格式错误")
                    continue
                
                name = channel_config.get("name", f"通道{bit_str}")
                duration = channel_config.get("duration", 0)
                
                if name in used_names:
                    self.validation_errors.append(f"通道名称重复: {name}")
                used_names.add(name)
                
                if duration <= 0:
                    self.validation_errors.append(f"通道{name}持续时间必须大于0")
                    
            except ValueError:
                self.validation_errors.append(f"无效的通道位值: {bit_str}")
    
    def _validate_processing_config(self):
        """验证处理配置"""
        processing_config = self.config_data.get("processing_config", {})
        
        alignment_time = processing_config.get("alignment_time")
        # alignment_time允许为负数，用于向前校正时间基准
        if alignment_time is not None and not isinstance(alignment_time, (int, float)):
            self.validation_errors.append("alignment_time必须为数字类型")
        
        alignment_channel = processing_config.get("alignment_channel") 
        if alignment_channel is not None:
            channel_mapping = self.config_data.get("channel_mapping", {})
            if str(alignment_channel) not in channel_mapping:
                self.validation_errors.append(f"alignment_channel {alignment_channel} 在通道映射中不存在")
    
    def get_channel_configs(self) -> Dict[int, ChannelConfig]:
        """
        获取通道配置对象
        
        Returns:
            Dict[int, ChannelConfig]: 通道位值到配置对象的映射
        """
        channel_mapping = self.config_data.get("channel_mapping", {})
        configs = {}
        
        for bit_str, config_dict in channel_mapping.items():
            try:
                bit_value = int(bit_str)
                configs[bit_value] = ChannelConfig(
                    name=config_dict.get("name", f"通道{bit_str}"),
                    bit_position=bit_value,  # 保持这个字段用于兼容，实际应该用bit_value
                    duration=float(config_dict.get("duration", 1.0)),
                    description=config_dict.get("description"),
                    enabled=config_dict.get("enabled", True)
                )
            except (ValueError, TypeError) as e:
                print(f"跳过无效通道配置 {bit_str}: {e}")
        
        return configs
    
    def get_file_config(self) -> FileConfig:
        """获取文件配置对象"""
        file_config = self.config_data.get("file_config", {})
        return FileConfig(
            data_path=file_config.get("data_path", ""),
            csv_path=file_config.get("csv_path"),
            audio_path=file_config.get("audio_path"),
            video_path=file_config.get("video_path"),
            output_dir=file_config.get("output_dir")
        )
    
    def get_processing_config(self) -> ProcessingConfig:
        """获取处理配置对象"""
        processing_config = self.config_data.get("processing_config", {})
        return ProcessingConfig(
            alignment_time=processing_config.get("alignment_time"),
            alignment_channel=processing_config.get("alignment_channel"),
            time_window_before=processing_config.get("time_window_before", 0.0),
            time_window_after=processing_config.get("time_window_after", 0.0),
            sampling_rate_check=processing_config.get("sampling_rate_check", True)
        )
    
    def get_paradigm_type(self) -> str:
        """
        获取范式类型
        
        Returns:
            str: 范式类型 ("auto", "movement", "corpus", "port")
        """
        return self.config_data.get("paradigm_type", "auto")
    
    def update_channel_duration(self, channel_bit: int, duration: float) -> bool:
        """
        更新通道持续时间
        
        Args:
            channel_bit: 通道位值
            duration: 新的持续时间
            
        Returns:
            bool: 是否成功更新
        """
        channel_mapping = self.config_data.get("channel_mapping", {})
        bit_str = str(channel_bit)
        
        if bit_str in channel_mapping:
            channel_mapping[bit_str]["duration"] = duration
            return True
        return False
    
    def generate_config_template(self, output_path: str = "config_template.json") -> bool:
        """
        生成配置文件模板
        
        Args:
            output_path: 输出路径
            
        Returns:
            bool: 是否成功生成
        """
        try:
            template = self.DEFAULT_CONFIG.copy()
            template["_comments"] = {
                "paradigm_type": "实验范式类型: auto(有CSV文件=movement,无CSV=port)/movement(运动)/corpus(语料)/port(端口)",
                "file_config": "文件路径配置",
                "data_path": "必需 - 数据文件路径(支持.wl或.pkl格式)",
                "csv_path": "可选 - 范式CSV文件路径，运动范式需要列: order,stimuli,atext；语料范式需要列: corpus_order,wl_corpus",
                "audio_path": "可选 - 音频文件路径(.wav格式)",
                "video_path": "可选 - 视频文件路径(预留字段)",
                "output_dir": "可选 - 输出目录，不指定则使用数据文件所在目录",
                "channel_mapping": "数字输入通道位值到配置的映射",
                "name": "通道显示名称",
                "duration": "该通道标记的持续时间(秒)，用于数据分割",
                "description": "通道功能描述说明",
                "enabled": "是否启用该通道进行处理(true/false)",
                "processing_config": "数据处理和时间同步配置",
                "alignment_time": "时间对齐偏移量(秒)，可为负值，用于校正时间基准",
                "alignment_channel": "对齐参考通道的位值，用于多通道时间同步",
                "time_window_before": "标记前的时间窗口(秒)，预留字段",
                "time_window_after": "标记后的时间窗口(秒)，预留字段",
                "sampling_rate_check": "是否检查采样率一致性(true/false)"
            }
            
            with open(output_path, 'w', encoding='utf-8') as f:
                json.dump(template, f, indent=4, ensure_ascii=False)
            
            print(f"配置模板已生成: {output_path}")
            return True
            
        except Exception as e:
            print(f"生成配置模板失败: {e}")
            return False
    
    def print_current_config(self):
        """打印当前配置的摘要"""
        print("\n" + "="*50)
        print("当前配置摘要")
        print("="*50)
        
        # 范式类型
        paradigm_type = self.get_paradigm_type()
        print(f"范式类型: {paradigm_type}")
        
        # 文件配置
        file_config = self.get_file_config()
        print(f"数据文件: {file_config.data_path}")
        print(f"CSV文件: {file_config.csv_path or '未配置'}")
        print(f"音频文件: {file_config.audio_path or '未配置'}")
        
        # 通道配置
        print(f"\n通道配置:")
        channel_configs = self.get_channel_configs()
        for bit_value, config in sorted(channel_configs.items()):
            status = "启用" if config.enabled else "禁用"
            print(f"  位{bit_value} -> {config.name}: {config.duration}秒 ({status})")
        
        # 处理配置
        proc_config = self.get_processing_config()
        print(f"\n处理配置:")
        print(f"  时间对齐: {proc_config.alignment_time or '不使用'}")
        print(f"  对齐通道: {proc_config.alignment_channel or '自动'}")
        
        print("="*50)
    
    def get_legacy_port_durations(self) -> Dict[str, float]:
        """
        获取兼容旧代码的端口持续时间字典
        
        Returns:
            Dict[str, float]: 通道名称到持续时间的映射
        """
        channel_configs = self.get_channel_configs()
        return {config.name: config.duration 
                for config in channel_configs.values() if config.enabled}
    
    def get_legacy_port_bit_mapping(self) -> Dict[int, str]:
        """
        获取兼容旧代码的端口位映射
        
        Returns:
            Dict[int, str]: 端口位到通道名称的映射
        """
        channel_configs = self.get_channel_configs()
        return {bit_value: config.name 
                for bit_value, config in channel_configs.items() if config.enabled}


def main():
    """测试配置管理器"""
    print("配置管理器测试")
    
    # 创建配置管理器
    config_manager = ConfigManager("test_config.json")
    
    # 生成模板
    config_manager.generate_config_template()
    
    # 打印配置
    config_manager.print_current_config()
    
    # 测试更新
    config_manager.update_channel_duration(1, 5.0)
    config_manager.save_config()
    
    print("\n测试完成！")

if __name__ == "__main__":
    main()